Skip to content

feat: add MSSQL/Fabric support to data-parity skill#705

Open
suryaiyer95 wants to merge 15 commits intomainfrom
feat/mssql-fabric-data-parity
Open

feat: add MSSQL/Fabric support to data-parity skill#705
suryaiyer95 wants to merge 15 commits intomainfrom
feat/mssql-fabric-data-parity

Conversation

@suryaiyer95
Copy link
Copy Markdown
Contributor

@suryaiyer95 suryaiyer95 commented Apr 13, 2026

Summary

  • SQL Server driver: full connect/execute/listSchemas/listTables/describeTable/close with T-SQL TOP injection, sys.* catalog queries, and row flattening for unnamed columns
  • Azure AD authentication: 7 flows (default, password, access-token, service-principal-secret, msi-vm, msi-app-service) with shorthand aliases (cli, default, password, service-principal, msi)
  • Dialect mapping: sqlserver/mssqltsql, fabricfabric with DATETRUNC() and CONVERT(DATE, ..., 23) for date partitioning
  • mssql v12 upgrade: ConnectionPool isolation for concurrent connections, unnamed-column array flattening, synthetic column name fallback
  • SKILL.md documentation: Fabric connection config, Azure AD auth types, algorithm behavior, schema inspection queries
  • 28 driver unit tests + 9 dialect mapping tests

Key files

Area Files
Driver packages/drivers/src/sqlserver.ts, packages/drivers/test/sqlserver-unit.test.ts
Dialect mapping packages/opencode/src/altimate/native/connections/data-diff.ts
Tool registration packages/opencode/src/altimate/tools/data-diff.ts
Skill docs .opencode/skills/data-parity/SKILL.md

Test plan

  • bun test packages/drivers/test/sqlserver-unit.test.ts — 28 tests pass
  • bun test packages/opencode/test/altimate/data-diff-dialect.test.ts — 9 tests pass
  • Integration test against real MSSQL/Fabric instance
  • Verify Azure AD auth flows with actual Azure credentials

🤖 Generated with Claude Code


Summary by cubic

Adds full MSSQL and Microsoft Fabric support to the data-parity skill with Azure AD auth, T‑SQL/Fabric dialect mapping, and an upgraded mssql v12 driver. Also fixes cross‑warehouse SQL‑query mode by injecting side‑specific CTEs to avoid T‑SQL/Fabric parse errors.

  • New Features

    • Data-parity: full MSSQL/Fabric support (auto, joindiff, hashdiff, profile, cascade), date partitioning via DATETRUNC() and CONVERT(DATE, ..., 23), robust SQL‑vs‑table detection (keyword + whitespace), and dialect mapping sqlserver/mssqltsql, fabricfabric, postgresqlpostgres, mariadbmysql.
    • SQL Server driver: 7 Azure AD flows with shorthands (default/CLI, password, service-principal, msi); TLS forced for AAD; token auto‑acquisition for default/CLI and when azure-active-directory-access-token is set without a token; explicit ConnectionPool isolation; T‑SQL TOP injection with ExecuteOptions.noLimit; stable result shaping for unnamed columns and synthetic names when needed.
    • Registry/config/docs: register fabric to reuse the SQL Server driver; normalize aliases for authentication and Azure fields; upgrade mssql to v12; make @azure/identity an optional peer dependency; SKILL docs add minimum versions and a Fabric connection guide; lockfile regenerated.
  • Bug Fixes

    • Cross-warehouse SQL-query mode: side‑aware CTE injection (source vs target) to prevent T‑SQL/Fabric from parsing the other side’s CTE and failing with “Invalid object name”.
    • Limits and detection: noLimit bypasses injected TOP; stricter SQL‑vs‑table detection.
    • Results and partitions: flatten only the empty‑key array for unnamed columns; preserve underscore‑prefixed columns; escape single quotes and cast partition dates with CONVERT(DATE, ..., 23); show “(no values)” when diff rows have no values; add fabric to password-driver validation.

Written for commit 1977232. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added support for Microsoft Fabric as a connection type with full authentication and schema introspection capabilities.
    • Extended Azure AD authentication options including access tokens, service principal secrets, and managed identity flows.
    • Enhanced SQL Server/Fabric schema inspection with improved system catalog queries.
  • Bug Fixes

    • Improved SQL result handling and column mapping for complex query results.
    • Enhanced data diff operations with SQL Server/Fabric-specific date handling and partition filtering.
  • Tests

    • Added comprehensive test coverage for SQL Server driver authentication and connection scenarios.
    • Added validation tests for Fabric connections, dialect mapping, and CTE injection.

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR adds comprehensive support for Microsoft Fabric as a warehouse connection type. Key changes include updating the SQL Server driver to support Azure AD authentication flows using @azure/identity, extending connection configuration normalization to handle authentication aliases, implementing warehouse-to-dialect mapping for data-diff operations, and adding SQL Server/Fabric-specific date handling. Dependencies are updated (mssql from ^11 to ^12, @azure/identity added as optional peer), and extensive unit and integration tests are added.

Changes

Cohort / File(s) Summary
SQL Server Driver & Dependencies
packages/drivers/package.json, packages/drivers/src/sqlserver.ts, packages/drivers/src/normalize.ts
Updated mssql dependency to ^12.0.0, added @azure/identity as optional peer dependency. Extended sqlserver.ts with Azure AD authentication support including token acquisition via @azure/identity with CLI fallback (az account get-access-token), connection pool handling, and normalized auth type mappings. Enhanced result flattening logic for positional/aggregate columns. Extended normalize.ts alias mapping to include authentication fields (authentication, azure_tenant_id, azure_client_id, azure_client_secret, access_token) and applied SQL Server aliases to fabric driver type.
Data-Diff Native Connections
packages/opencode/src/altimate/native/connections/data-diff.ts, packages/opencode/src/altimate/native/connections/registry.ts
Added warehouseTypeToDialect() function and WAREHOUSE_TO_DIALECT mapping to resolve warehouse types to Rust-compatible dialect names. Improved SQL-vs-table detection requiring both keyword match and whitespace. Extended resolveTableSources return shape with side-specific CTE prefixes. Implemented cross-warehouse detection with side-specific CTE injection. Added SQL Server/Fabric-specific date handling using DATETRUNC() and CONVERT(DATE, ..., 23) for partition filtering. Registered fabric connection type in driver registry with password validation.
Data-Diff Formatting
packages/opencode/src/altimate/tools/data-diff.ts
Updated diff sample rendering to display "(no values)" when row values field is missing.
SQL Server Driver Unit Tests
packages/drivers/test/sqlserver-unit.test.ts
Added comprehensive Bun unit test suite covering SQL query rewriting (TOP injection detection), truncation detection, Azure AD authentication configuration across multiple flows (azure-active-directory-password, access-token, service-principal-secret, msi-vm, msi-app-service), shorthand auth mapping, schema introspection methods, connection lifecycle idempotency, and result formatting including empty-string column flattening.
Data-Diff & Connection Tests
packages/opencode/test/altimate/connections.test.ts, packages/opencode/test/altimate/data-diff-cte.test.ts, packages/opencode/test/altimate/data-diff-dialect.test.ts, packages/opencode/test/altimate/driver-normalize.test.ts
Added test coverage for Fabric connection type in registry, warehouse-to-dialect mapping across warehouse types (with case-insensitive handling), CTE resolution and side-specific injection for cross-warehouse queries, and SQL Server/Fabric configuration field normalization.
Documentation
.opencode/skills/data-parity/SKILL.md
Added documentation for SQL Server/Fabric schema introspection using system catalogs, minimum version requirements (DATETRUNC() availability), supported warehouse configurations, authentication options (including Fabric auth type mappings), Fabric connection parameters, and algorithm behavior/dialect mapping.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Driver as SQL Server Driver
    participant AzureId as `@azure/identity`
    participant AzureAD as Azure AD
    participant CLI as CLI (az command)
    participant MsSQL as mssql Library
    
    App->>Driver: connect(config with Azure AD auth)
    Driver->>Driver: Normalize auth type<br/>(e.g., "service-principal" → "azure-active-directory-service-principal-secret")
    alt `@azure/identity` Available
        Driver->>AzureId: DefaultAzureCredential<br/>.getToken()
        AzureId->>AzureAD: Request access token
        AzureAD-->>AzureId: Return access token
        AzureId-->>Driver: Token acquired
    else `@azure/identity` Fails
        Driver->>CLI: execSync("az account<br/>get-access-token")
        CLI->>AzureAD: Request token via CLI
        AzureAD-->>CLI: Return token
        CLI-->>Driver: Token string
    end
    Driver->>MsSQL: new ConnectionPool(config<br/>with auth + token)
    MsSQL->>AzureAD: Authenticate with token
    AzureAD-->>MsSQL: Connection established
    MsSQL-->>Driver: Pool connected
    Driver-->>App: Connected driver instance
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • kulvirgit
  • mdesmet

Poem

🐰 A Fabric thread we've gently spun,
Azure tokens dance and run,
From CLI's whisper to credential's call,
SQL Server welcomes one and all,
Cross-warehouse queries take their flight! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive and well-structured, covering all required template sections: Summary (with detailed feature list), Test Plan (with test runs documented), and Checklist items. However, it lacks the mandatory 'PINEAPPLE' marker required for AI-generated content per the repository template. Add 'PINEAPPLE' at the very top of the PR description before any other content, as required by the repository template for AI-generated contributions.
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding MSSQL and Fabric support to the data-parity skill. It is concise, specific, and directly related to the primary changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mssql-fabric-data-parity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 30 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/native/connections/data-diff.ts">

<violation number="1" location="packages/opencode/src/altimate/native/connections/data-diff.ts:266">
P2: Missing single-quote escaping on `partitionValue` in the date-mode branches. The categorical mode (6 lines above) escapes with `.replace(/'/g, "''")`, but none of the date-mode `switch` cases do. Apply the same escaping for consistency and defense in depth.</violation>

<violation number="2" location="packages/opencode/src/altimate/native/connections/data-diff.ts:489">
P2: `partitionColumn` is not identifier-quoted in `buildPartitionDiscoverySQL`, but it is quoted in `buildPartitionWhereClause`. If the column name is a reserved word (e.g. `date`, `order`), the discovery query will produce a syntax error. Quote the column consistently between both functions.</violation>
</file>

<file name="packages/opencode/src/altimate/native/connections/registry.ts">

<violation number="1" location="packages/opencode/src/altimate/native/connections/registry.ts:125">
P2: `fabric` is missing from the `PASSWORD_DRIVERS` set. Since it maps to the same sqlserver driver as `sqlserver`/`mssql` (both present in the set), `fabric` should also be included to get the same friendly error when `password` is not a string.</violation>
</file>

<file name="packages/opencode/src/altimate/tools/data-diff.ts">

<violation number="1" location="packages/opencode/src/altimate/tools/data-diff.ts:206">
P2: When `d.values` is nullish, `d.values?.join(" | ")` evaluates to `undefined`, which the template literal coerces to the string `"undefined"`. The output would read e.g. `[source only] undefined`. Use a fallback to produce a sensible message.</violation>
</file>

<file name="packages/drivers/src/sqlserver.ts">

<violation number="1" location="packages/drivers/src/sqlserver.ts:148">
P1: `flattenRow` flattens all array values, but only the empty-string key (`""`) holds mssql's merged unnamed columns. A legitimate array column value (e.g. from JSON aggregation) will be incorrectly spread, corrupting the row data and misaligning columns. Restrict flattening to the `""` key only.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread packages/drivers/src/sqlserver.ts Outdated
Comment thread packages/opencode/src/altimate/native/connections/data-diff.ts Outdated
Comment thread packages/opencode/src/altimate/native/connections/data-diff.ts
Comment thread packages/opencode/src/altimate/native/connections/registry.ts
Comment thread packages/opencode/src/altimate/tools/data-diff.ts Outdated
@suryaiyer95 suryaiyer95 force-pushed the feat/mssql-fabric-data-parity branch from 403477e to 811c2be Compare April 13, 2026 17:36
@anandgupta42
Copy link
Copy Markdown
Contributor

@suryaiyer95 can you address code review comments?

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/drivers/src/sqlserver.ts">

<violation number="1" location="packages/drivers/src/sqlserver.ts:90">
P2: Avoid `execSync` in the async connection path; it can block the event loop for up to the CLI timeout.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread packages/drivers/src/sqlserver.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/native/connections/data-diff.ts">

<violation number="1" location="packages/opencode/src/altimate/native/connections/data-diff.ts:819">
P2: `crossWarehouse` is computed from unresolved params, so an omitted `source_warehouse` can be misclassified as cross-warehouse when both sides actually run on the same default warehouse.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread packages/opencode/src/altimate/native/connections/data-diff.ts
@suryaiyer95 suryaiyer95 changed the base branch from feat/data-parity-skill-improvements to main April 17, 2026 05:22
suryaiyer95 and others added 15 commits April 16, 2026 22:26
Bun's runtime never fires native addon async callbacks, so the async
`new duckdb.Database(path, opts, callback)` form would hit the 2-second
timeout fallback on every connection attempt.

Switch to the synchronous constructor form `new duckdb.Database(path)` /
`new duckdb.Database(path, opts)` which throws on error and completes
immediately in both Node and bun runtimes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The async callback form with 2s fallback was already working correctly
at e3df5a4. The timeout was caused by a missing duckdb .node binary,
not a bun incompatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `warehouseTypeToDialect()` mapping: sqlserver→tsql, mssql→tsql,
  fabric→fabric, postgresql→postgres, mariadb→mysql. Fixes critical
  serde mismatch where Rust engine rejects raw warehouse type names.
- Update both `resolveDialect()` functions to use the mapping
- Add MSSQL/Fabric cases to `dateTruncExpr()` — DATETRUNC(DAY, col)
- Add locale-safe date literal casting via CONVERT(DATE, ..., 23)
- Register `fabric` in DRIVER_MAP (reuses sqlserver TDS driver)
- Add `fabric` normalize aliases in normalize.ts
- Add 15 SQL Server driver unit tests (TOP injection, truncation,
  schema introspection, connection lifecycle, result format)
- Add 9 dialect mapping unit tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Support all 7 Azure AD / Entra ID auth types in `sqlserver.ts`:
  `azure-active-directory-password`, `access-token`, `service-principal-secret`,
  `msi-vm`, `msi-app-service`, `azure-active-directory-default`, `token-credential`
- Force TLS encryption for all Azure AD connections
- Dynamic import of `@azure/identity` for `DefaultAzureCredential`
- Add normalize aliases for Azure AD config fields (`authentication`,
  `azure_tenant_id`, `azure_client_id`, `azure_client_secret`, `access_token`)
- Add `fabric: SQLSERVER_ALIASES` to DRIVER_ALIASES
- Add 10 Azure AD unit tests covering all auth flows, encryption,
  and `DefaultAzureCredential` with managed identity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…LL.md

- Add SQL Server / Fabric schema inspection query in Step 2
- Add "SQL Server and Microsoft Fabric" section with:
  - Supported configurations table (sqlserver, mssql, fabric)
  - Fabric connection guide with Azure AD auth types
  - Algorithm behavior notes (joindiff vs hashdiff selection)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rscore column filter

- **Azure AD auth**: Pass `azure-active-directory-*` types directly to tedious
  instead of constructing `DefaultAzureCredential` ourselves. Tedious imports
  `@azure/identity` internally and creates credentials — avoids bun CJS/ESM
  `isTokenCredential` boundary issue that caused "not an instance of the token
  credential class" errors.
- **Auth shorthands**: Map `CLI`, `default`, `password`, `service-principal`,
  `msi`, `managed-identity` to their full tedious type names.
- **Column filter**: Remove `_.startsWith("_")` filter from `execute()` result
  columns — it stripped legitimate aliases like `_p` used by partition discovery,
  causing partitioned diffs to return empty results.
- **Tests**: Remove `@azure/identity` mock (no longer imported by driver),
  update auth assertions, add shorthand mapping tests, fix column filter test.
- **Verified**: All 97 driver tests pass. Full data-diff pipeline tested against
  real MSSQL server (profile, joindiff, auto, where_clause, partitioned).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lattening

- Upgrade `mssql` from v11 to v12 (`tedious` 18 → 19)
- Use explicit `ConnectionPool` instead of global `mssql.connect()` to
  isolate multiple simultaneous connections
- Flatten unnamed column arrays — `mssql` merges unnamed columns (e.g.
  `SELECT COUNT(*), SUM(...)`) into a single array under the empty-string
  key; restore positional column values
- Proper column name resolution: compare `namedKeys.length` against
  flattened row length, fall back to synthetic `col_0`, `col_1`, etc.
- Update test mock to export `ConnectionPool` class and `createMockPool`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tions

Use ternary expressions (`x ? {...} : {}`) instead of short-circuit
(`x && {...}`) to avoid spreading a boolean value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- P1: restrict `flattenRow` to only spread the empty-string key (`""`)
  where mssql merges unnamed columns, preserving legitimate array values
- P2: escape single quotes in `partitionValue` for date-mode branches in
  `buildPartitionWhereClause` (categorical mode already escaped)
- P2: add `fabric` to `PASSWORD_DRIVERS` set in registry for consistent
  password validation alongside `sqlserver`/`mssql`
- P2: fallback to `"(no values)"` when `d.values` is nullish to prevent
  template literal coercing `undefined` to the string `"undefined"`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sqlserver-unit: 3 tests for unnamed column flattening — verifies only
  the empty-string key is spread, legitimate named arrays are preserved
- driver-normalize: fabric type uses SQLSERVER_ALIASES (server → host,
  trustServerCertificate → trust_server_certificate)
- connections: fabric type is recognized in DRIVER_MAP and listed correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Minimum Version Requirements" table to SKILL.md covering SQL Server
  2022+, mssql v12, and @azure/identity v4 with rationale for each
- Document auth shorthands (CLI, default, password, service-principal, msi)
- Move @azure/identity from dependencies to optional peerDependencies so
  it is NOT installed by default — only required for Azure AD auth
- Add runtime check in sqlserver driver: if Azure AD auth type is requested
  but @azure/identity is missing, throw a clear install instruction error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…solution

- For `azure-active-directory-default` (CLI/default auth), acquire token
  ourselves instead of delegating to tedious's internal `@azure/identity`
- Strategy: try `DefaultAzureCredential` first, fall back to `az` CLI subprocess
- Bypasses Bun resolving `@azure/identity` to browser bundle where
  `DefaultAzureCredential` is a non-functional stub
- Also bypasses CJS/ESM `isTokenCredential` boundary mismatch
- All 31 driver unit tests pass, verified against real Fabric endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oken` when none supplied

The `azure-active-directory-access-token` branch passed
`token: config.token ?? config.access_token` to tedious. When neither
field was set on a connection (e.g. a `fabric-migration` entry that
declared the auth type but no token), tedious threw:

    TypeError: The "config.authentication.options.token" property
    must be of type string

This blocked any Fabric/MSSQL config that relied on ambient credentials
(Azure CLI / managed identity) but used the explicit
`azure-active-directory-access-token` type instead of the `default`
shorthand.

Refactor token acquisition (`DefaultAzureCredential` → `az` CLI
fallback) into a shared `acquireAzureToken()` helper used by both the
`default` path and the `access-token` path when no token was supplied.
Callers that pass an explicit token are unchanged.

Also harden `mock.module("node:child_process", ...)` in
`sqlserver-unit.test.ts` to spread the real module so sibling tests in
the same `bun test` run keep access to `spawn` / `exec` / `fork`.

Tests: 110 pass, 0 fail in `packages/drivers`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ry mode

When `source` and `target` are both SQL queries, `resolveTableSources` wraps
them as `__diff_source` / `__diff_target` CTEs and the executor prepends the
combined `WITH …` block to every engine-emitted task. T-SQL and Fabric
parse-bind every CTE body even when unreferenced, so a task routed to the
source warehouse failed to resolve the target-only base table referenced
inside the unused `__diff_target` CTE (and vice versa), producing
`Invalid object name` errors from the wrong warehouse.

Return side-specific prefixes from `resolveTableSources` alongside the
combined one, and have the executor loop in `runDataDiff` pick the source
or target prefix per task when `source_warehouse !== target_warehouse`.
Same-warehouse behaviour is unchanged.

Adds `data-diff-cte.test.ts` covering plain-name passthrough, both-query
wrapping, side-specific CTE isolation, and CTE merging with engine-emitted
`WITH` clauses (10 tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 333a45c moved `@azure/identity` from `optionalDependencies` to
`peerDependencies` with `optional: true` in `packages/drivers/package.json`,
but the lockfile was not regenerated. That left CI under `--frozen-lockfile`
broken and made fresh installs silently diverge from the committed state.

Running `bun install` brings the lockfile in sync: `@azure/identity` is
recorded as an optional peer, and its transitive pins (`@azure/msal-browser`,
`@azure/msal-common`, `@azure/msal-node`) re-resolve to the versions required
by `tedious` and `snowflake-sdk`, matching the reachable runtime surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@suryaiyer95 suryaiyer95 force-pushed the feat/mssql-fabric-data-parity branch from 52fe09e to 1977232 Compare April 17, 2026 05:26
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (4)
packages/opencode/src/altimate/native/connections/data-diff.ts (1)

815-820: Cross-warehouse detection is a purely textual compare.

sourceWarehouse !== targetWarehouse treats undefined vs an explicit name as "cross-warehouse" even when the explicit name is the default (first) warehouse. The side-specific CTE injection is a strict superset of the combined prefix, so correctness isn't affected — just a minor inefficiency (slightly narrower CTE sent). Not blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/altimate/native/connections/data-diff.ts` around lines
815 - 820, Summary: crossWarehouse currently does a raw textual compare
(sourceWarehouse !== targetWarehouse) which can misclassify undefined vs an
explicit default warehouse name as cross-warehouse. Fix: canonicalize both sides
before comparing by resolving undefined/empty values to the effective/default
warehouse name used elsewhere (use the same resolver used for
connection/effective settings) instead of comparing raw params.source_warehouse
and params.target_warehouse; update the variables referenced (sourceWarehouse,
targetWarehouse, crossWarehouse) so crossWarehouse = resolvedSourceWarehouse !==
resolvedTargetWarehouse. Ensure you call the existing resolution helper (or add
one) rather than inventing a new default string.
packages/drivers/test/sqlserver-unit.test.ts (1)

483-489: Assertion gap lets the "mixed named + unnamed" column-naming bug slip through.

This test verifies rows for { name: "alice", "": [42] } but doesn't assert result.columns. Under the current implementation (sqlserver.ts Lines 210-217), mixing a named column with multiple unnamed values (e.g. { name: "alice", "": [42, 100] }) collapses the header to ["col_0", "col_1", "col_2"] and drops name. Adding a case with ≥ 2 unnamed values and asserting columns would have caught that and will guard whichever fix lands.

♻️ Suggested additional case
     test("handles mix of named and unnamed columns", async () => {
       mockQueryResult = {
         recordset: [{ name: "alice", "": [42] }],
       }
       const result = await connector.execute("SELECT * FROM t")
       expect(result.rows).toEqual([["alice", 42]])
     })
+
+    test("preserves named-column header when mixed with multiple unnamed columns", async () => {
+      // SELECT name, COUNT(*), SUM(x) FROM t → { name: "alice", "": [42, 100] }
+      mockQueryResult = {
+        recordset: [{ name: "alice", "": [42, 100] }],
+      }
+      const result = await connector.execute("SELECT name, COUNT(*), SUM(x) FROM t")
+      expect(result.rows).toEqual([["alice", 42, 100]])
+      expect(result.columns[0]).toBe("name")
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/test/sqlserver-unit.test.ts` around lines 483 - 489, Add a
new test variant in packages/drivers/test/sqlserver-unit.test.ts that uses a
recordset like { name: "alice", "": [42, 100] } (i.e., multiple unnamed values)
and assert both result.rows and result.columns so the header preserves the named
column "name" and includes generated column names for the unnamed values (e.g.,
["name","col_1","col_2"]); this will surface the bug referenced around
sqlserver.ts (logic near lines handling column mapping) and prevent regressions
in connector.execute's column assembly.
packages/opencode/test/altimate/data-diff-dialect.test.ts (1)

51-54: Consider expanding case-insensitivity coverage.

The case-insensitivity block only exercises sqlserver and postgresql. Since mssql, fabric, and mariadb go through the same normalization path, a one-line additional assertion (e.g., MSSQLtsql, FABRICfabric) would guard against a future regression where only some branches get lowercased.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/altimate/data-diff-dialect.test.ts` around lines 51 -
54, Add assertions to the "handles uppercase input" test to cover other inputs
that share the normalization path so case-insensitivity can't regress: update
the test block in packages/opencode/test/altimate/data-diff-dialect.test.ts that
calls warehouseTypeToDialect to also assert uppercase variants such as "MSSQL"
-> "tsql", "FABRIC" -> "fabric" and "MARIADB" -> "mariadb" (or whichever
expected dialects map in warehouseTypeToDialect) so all branches that are
lowercased are exercised.
packages/drivers/src/sqlserver.ts (1)

89-100: execSync blocks the event loop during connect().

execSync("az account get-access-token ...") with a 15 s timeout runs synchronously on the main thread inside an async function — any other pending I/O on the runtime is stalled while Azure CLI spins up (typically 0.5–2 s, occasionally much longer). Prefer the async execFile (or promisify(exec)) so the token fetch cooperates with the event loop.

♻️ Suggested change
-            try {
-              const { execSync } = await import("node:child_process")
-              const out = execSync(
-                "az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv",
-                { encoding: "utf-8", timeout: 15000, stdio: ["pipe", "pipe", "pipe"] },
-              ).trim()
-              if (out) token = out
-            } catch {
-              // az CLI not installed or not logged in
-            }
+            try {
+              const { execFile } = await import("node:child_process")
+              const { promisify } = await import("node:util")
+              const run = promisify(execFile)
+              const { stdout } = await run(
+                "az",
+                ["account", "get-access-token", "--resource", "https://database.windows.net/", "--query", "accessToken", "-o", "tsv"],
+                { encoding: "utf-8", timeout: 15000 },
+              )
+              const out = stdout.trim()
+              if (out) token = out
+            } catch {
+              // az CLI not installed or not logged in
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 89 - 100, The current token
fetch inside connect() uses sync child process invocation (execSync) which
blocks the event loop; replace it with an asynchronous call (e.g., use
child_process.execFile or promisified exec via util.promisify) so the Azure CLI
call runs without blocking. Locate the block that imports execSync and sets
token (the token variable and the try/catch that calls "az account
get-access-token ...") and change it to call the async API (await the
execFile/promisified exec) with the same args and timeout, handle stdout.trim()
to set token, and preserve the existing catch behavior for missing CLI or auth
failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.opencode/skills/data-parity/SKILL.md:
- Around line 453-458: The fenced code block showing the pseudo-YAML config (the
block containing type: "fabric", host:
"<workspace-id>-<item-id>.datawarehouse.fabric.microsoft.com", database:
"<warehouse-name>", authentication: "azure-active-directory-default") is missing
a language tag; change the opening fence from ``` to ```yaml (or ```text) so
markdownlint MD040 is satisfied and the block is treated as YAML.

In `@packages/drivers/package.json`:
- Around line 20-33: Remove the redundant optional peer dependency entry for
"@azure/identity" from package.json: delete the "@azure/identity" key inside the
"peerDependencies" object and its corresponding entry in "peerDependenciesMeta"
since "mssql"/"tedious" already bundles it; update the package.json so only the
other dependencies (e.g., "mssql", "oracledb", "duckdb", "mongodb",
"@clickhouse/client") remain and no "@azure/identity" references exist under
"peerDependencies" or "peerDependenciesMeta".

In `@packages/drivers/src/sqlserver.ts`:
- Around line 53-54: The code calls rawAuth.toLowerCase() without ensuring
rawAuth is a string, causing a TypeError for non-string authentication values;
update the computation of authType in the sqlserver driver to first check typeof
rawAuth === "string" before using toLowerCase and looking up AUTH_SHORTHANDS,
e.g. only apply AUTH_SHORTHANDS[rawAuth.toLowerCase()] when rawAuth is a string,
otherwise pass rawAuth through (or leave undefined) so non-string auth blocks
(config.authentication) don't blow up at runtime; adjust the logic around
rawAuth, authType, and AUTH_SHORTHANDS to be type-safe and explicit.
- Around line 56-158: The code path that handles authType starting with
"azure-active-directory" (see authType variable and mssqlConfig.authentication
assignments) does not handle unknown subtypes and can leave mssqlConfig without
authentication; add a terminal else branch after the existing
azure-active-directory-* branches that throws a clear Error (e.g. mentioning the
unsupported authType) so unknown or misspelled Azure AD subtypes fail fast;
ensure the thrown error references authType so callers get an explicit message
instead of a later opaque tedious error.
- Around line 76-87: The empty catch around the `@azure/identity` import and
credential.getToken is hiding real authentication errors; change the catch to
capture the thrown error (e.g., catch (err) { azureIdentityErr = err }) and
preserve that error in a local variable, then either append its message/details
to the final thrown error or log it at debug level when token remains undefined;
update the block that currently throws the generic "install `@azure/identity` or
run az login" message to include the captured azureIdentityErr (or its
message/stack) so real AAD/authentication failures are visible while still
tolerating missing-module/browser-bundle cases.
- Around line 208-217: The current columns logic replaces known named headers
when sampleFlat is longer (mixed named + unnamed aggregates); change the
construction in the columns branch to preserve the existing named prefix
(namedKeys) and only synthesize column names for the extra flattened entries
(e.g., generate col_i for indices >= namedKeys.length up to
sampleFlat.length-1), falling back to result.recordset.columns only when both
namedKeys and sampleFlat are empty; update the conditional around
sampleFlat/namedKeys where columns is assigned (the expression that uses
sampleFlat, namedKeys, recordset, and result.recordset.columns) so known headers
are retained and only the tail is renamed.
- Around line 162-167: Remove the fallback to the global shared pool to avoid
reintroducing cross-database isolation; in the block that currently checks
MssqlConnectionPool, ensure that if MssqlConnectionPool is falsy you throw an
explicit error instead of calling mssql.connect, i.e., keep the branch that
constructs and connects a new MssqlConnectionPool(mssqlConfig) and replace the
else branch with a throw that references MssqlConnectionPool and mssql.connect
in the message so reviewers can quickly locate and understand the change.

In `@packages/drivers/test/sqlserver-unit.test.ts`:
- Around line 154-159: The test "empty result returns correctly" uses a fake
mssql response shape by setting mockQueryResult.recordset_columns, but the
driver reads result.recordset?.columns; update the test to match real mssql
shape by removing recordset_columns and instead set mockQueryResult.recordset =
[] and, if you need to include column metadata, set
mockQueryResult.recordset.columns = {} (or leave absent for empty results) so
the driver code that checks result.recordset?.columns and the code paths in
connector.execute behave against the real response shape.

---

Nitpick comments:
In `@packages/drivers/src/sqlserver.ts`:
- Around line 89-100: The current token fetch inside connect() uses sync child
process invocation (execSync) which blocks the event loop; replace it with an
asynchronous call (e.g., use child_process.execFile or promisified exec via
util.promisify) so the Azure CLI call runs without blocking. Locate the block
that imports execSync and sets token (the token variable and the try/catch that
calls "az account get-access-token ...") and change it to call the async API
(await the execFile/promisified exec) with the same args and timeout, handle
stdout.trim() to set token, and preserve the existing catch behavior for missing
CLI or auth failures.

In `@packages/drivers/test/sqlserver-unit.test.ts`:
- Around line 483-489: Add a new test variant in
packages/drivers/test/sqlserver-unit.test.ts that uses a recordset like { name:
"alice", "": [42, 100] } (i.e., multiple unnamed values) and assert both
result.rows and result.columns so the header preserves the named column "name"
and includes generated column names for the unnamed values (e.g.,
["name","col_1","col_2"]); this will surface the bug referenced around
sqlserver.ts (logic near lines handling column mapping) and prevent regressions
in connector.execute's column assembly.

In `@packages/opencode/src/altimate/native/connections/data-diff.ts`:
- Around line 815-820: Summary: crossWarehouse currently does a raw textual
compare (sourceWarehouse !== targetWarehouse) which can misclassify undefined vs
an explicit default warehouse name as cross-warehouse. Fix: canonicalize both
sides before comparing by resolving undefined/empty values to the
effective/default warehouse name used elsewhere (use the same resolver used for
connection/effective settings) instead of comparing raw params.source_warehouse
and params.target_warehouse; update the variables referenced (sourceWarehouse,
targetWarehouse, crossWarehouse) so crossWarehouse = resolvedSourceWarehouse !==
resolvedTargetWarehouse. Ensure you call the existing resolution helper (or add
one) rather than inventing a new default string.

In `@packages/opencode/test/altimate/data-diff-dialect.test.ts`:
- Around line 51-54: Add assertions to the "handles uppercase input" test to
cover other inputs that share the normalization path so case-insensitivity can't
regress: update the test block in
packages/opencode/test/altimate/data-diff-dialect.test.ts that calls
warehouseTypeToDialect to also assert uppercase variants such as "MSSQL" ->
"tsql", "FABRIC" -> "fabric" and "MARIADB" -> "mariadb" (or whichever expected
dialects map in warehouseTypeToDialect) so all branches that are lowercased are
exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6589a3e0-5c1a-4ac6-bd8a-e405334413ca

📥 Commits

Reviewing files that changed from the base of the PR and between c438bb0 and 1977232.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .opencode/skills/data-parity/SKILL.md
  • packages/drivers/package.json
  • packages/drivers/src/normalize.ts
  • packages/drivers/src/sqlserver.ts
  • packages/drivers/test/sqlserver-unit.test.ts
  • packages/opencode/src/altimate/native/connections/data-diff.ts
  • packages/opencode/src/altimate/native/connections/registry.ts
  • packages/opencode/src/altimate/tools/data-diff.ts
  • packages/opencode/test/altimate/connections.test.ts
  • packages/opencode/test/altimate/data-diff-cte.test.ts
  • packages/opencode/test/altimate/data-diff-dialect.test.ts
  • packages/opencode/test/altimate/driver-normalize.test.ts

Comment on lines +453 to +458
```
type: "fabric"
host: "<workspace-id>-<item-id>.datawarehouse.fabric.microsoft.com"
database: "<warehouse-name>"
authentication: "azure-active-directory-default" # recommended
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add yaml (or text) language to the fenced block.

markdownlint MD040 flags missing language. Since the block is a pseudo-YAML config example, yaml reads best:

💚 Fix
-```
+```yaml
 type: "fabric"
 host: "<workspace-id>-<item-id>.datawarehouse.fabric.microsoft.com"
 database: "<warehouse-name>"
 authentication: "azure-active-directory-default"   # recommended
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 453-453: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.opencode/skills/data-parity/SKILL.md around lines 453 - 458, The fenced
code block showing the pseudo-YAML config (the block containing type: "fabric",
host: "<workspace-id>-<item-id>.datawarehouse.fabric.microsoft.com", database:
"<warehouse-name>", authentication: "azure-active-directory-default") is missing
a language tag; change the opening fence from ``` to ```yaml (or ```text) so
markdownlint MD040 is satisfied and the block is treated as YAML.

Comment on lines +20 to 33
"mssql": "^12.0.0",
"oracledb": "^6.0.0",
"duckdb": "^1.0.0",
"mongodb": "^6.0.0",
"@clickhouse/client": "^1.0.0"
},
"peerDependencies": {
"@azure/identity": ">=4.0.0"
},
"peerDependenciesMeta": {
"@azure/identity": {
"optional": true
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does mssql 12.x on npm bundle or peer-depend on @azure/identity, and what is the current minimum @azure/identity version for its azure-active-directory-* auth types?

💡 Result:

No, the mssql 12.x npm package does not bundle or peer-depend on @azure/identity. It depends on tedious ^19.0.0, which bundles @azure/identity ^4.2.1 as a regular dependency. For azure-active-directory-* auth types (such as azure-active-directory-default, azure-active-directory-password, azure-active-directory-access-token, azure-active-directory-msi-vm, azure-active-directory-msi-app-service, azure-active-directory-service-principal-secret), tedious handles the authentication internally using its bundled @azure/identity, so no separate installation or specific minimum version of @azure/identity is required by the user. The effective minimum version is whatever tedious 19.x requires, which is ^4.2.1.

Citations:


Remove @azure/identity from peerDependencies — it's redundant and already bundled by tedious.

mssql v12 depends on tedious ^19.0.0, which bundles @azure/identity ^4.2.1 as a regular dependency. Adding @azure/identity as an optional peer dependency in drivers/package.json is unnecessary; tedious handles Azure AD authentication internally without user intervention. Users installing mssql automatically get the correct @azure/identity version through the tedious dependency chain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/package.json` around lines 20 - 33, Remove the redundant
optional peer dependency entry for "@azure/identity" from package.json: delete
the "@azure/identity" key inside the "peerDependencies" object and its
corresponding entry in "peerDependenciesMeta" since "mssql"/"tedious" already
bundles it; update the package.json so only the other dependencies (e.g.,
"mssql", "oracledb", "duckdb", "mongodb", "@clickhouse/client") remain and no
"@azure/identity" references exist under "peerDependencies" or
"peerDependenciesMeta".

Comment on lines +53 to +54
const rawAuth = config.authentication as string | undefined
const authType = rawAuth ? (AUTH_SHORTHANDS[rawAuth.toLowerCase()] ?? rawAuth) : undefined
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Non-string authentication will throw on toLowerCase().

config.authentication is typed as unknown at the call site and is cast to string | undefined, but there's no runtime check. A caller passing a non-string (e.g. a config object, a null, or a pre-built { type, options } authentication block — which some users might try) will hit TypeError: rawAuth.toLowerCase is not a function instead of a meaningful driver error. A typeof rawAuth === "string" guard would keep the shorthand path strictly opt-in.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 53 - 54, The code calls
rawAuth.toLowerCase() without ensuring rawAuth is a string, causing a TypeError
for non-string authentication values; update the computation of authType in the
sqlserver driver to first check typeof rawAuth === "string" before using
toLowerCase and looking up AUTH_SHORTHANDS, e.g. only apply
AUTH_SHORTHANDS[rawAuth.toLowerCase()] when rawAuth is a string, otherwise pass
rawAuth through (or leave undefined) so non-string auth blocks
(config.authentication) don't blow up at runtime; adjust the logic around
rawAuth, authType, and AUTH_SHORTHANDS to be type-safe and explicit.

Comment on lines +56 to +158
if (authType?.startsWith("azure-active-directory")) {
;(mssqlConfig.options as any).encrypt = true

// Resolve a raw Azure AD access token.
// Used by both `azure-active-directory-default` and by
// `azure-active-directory-access-token` when no token was provided.
//
// We acquire the token ourselves rather than letting tedious do it because:
// 1. Bun can resolve @azure/identity to the browser bundle (inside
// tedious or even our own import), where DefaultAzureCredential
// is a non-functional stub that throws.
// 2. Passing a credential object via type:"token-credential" hits a
// CJS/ESM isTokenCredential boundary mismatch in Bun.
//
// Strategy: try @azure/identity first (works when module resolution
// is correct), fall back to shelling out to `az account get-access-token`
// (works everywhere Azure CLI is installed).
const acquireAzureToken = async (): Promise<string> => {
let token: string | undefined

try {
const azureIdentity = await import("@azure/identity")
const credential = new azureIdentity.DefaultAzureCredential(
config.azure_client_id
? { managedIdentityClientId: config.azure_client_id as string }
: undefined,
)
const tokenResponse = await credential.getToken("https://database.windows.net/.default")
token = tokenResponse?.token
} catch {
// @azure/identity unavailable or browser bundle — fall through
}

if (!token) {
try {
const { execSync } = await import("node:child_process")
const out = execSync(
"az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv",
{ encoding: "utf-8", timeout: 15000, stdio: ["pipe", "pipe", "pipe"] },
).trim()
if (out) token = out
} catch {
// az CLI not installed or not logged in
}
}

if (!token) {
throw new Error(
"Azure AD token acquisition failed. Either install @azure/identity (npm install @azure/identity) " +
"or log in with Azure CLI (az login).",
)
}
return token
}

if (authType === "azure-active-directory-default") {
mssqlConfig.authentication = {
type: "azure-active-directory-access-token",
options: { token: await acquireAzureToken() },
}
} else if (authType === "azure-active-directory-password") {
mssqlConfig.authentication = {
type: "azure-active-directory-password",
options: {
userName: config.user,
password: config.password,
clientId: config.azure_client_id,
tenantId: config.azure_tenant_id,
},
}
} else if (authType === "azure-active-directory-access-token") {
// If the caller supplied a token, use it; otherwise acquire one
// automatically (DefaultAzureCredential → az CLI).
const suppliedToken = (config.token ?? config.access_token) as string | undefined
mssqlConfig.authentication = {
type: "azure-active-directory-access-token",
options: { token: suppliedToken ?? (await acquireAzureToken()) },
}
} else if (
authType === "azure-active-directory-msi-vm" ||
authType === "azure-active-directory-msi-app-service"
) {
mssqlConfig.authentication = {
type: authType,
options: {
...(config.azure_client_id ? { clientId: config.azure_client_id } : {}),
},
}
} else if (authType === "azure-active-directory-service-principal-secret") {
mssqlConfig.authentication = {
type: "azure-active-directory-service-principal-secret",
options: {
clientId: config.azure_client_id,
clientSecret: config.azure_client_secret,
tenantId: config.azure_tenant_id,
},
}
}
} else {
// Standard SQL Server user/password
mssqlConfig.user = config.user
mssqlConfig.password = config.password
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unknown azure-active-directory-* subtype silently produces an auth-less config.

The chain at Lines 111-153 handles five specific Azure AD subtypes, but any other value starting with azure-active-directory- (e.g. a typo, or a future tedious subtype) skips all branches. Because the else at Line 154 is gated on the outer startsWith check, neither authentication nor user/password is set, and the pool connects with an incomplete config — producing an opaque tedious error far from the root cause. A terminal else { throw new Error(\Unsupported Azure AD auth type: ${authType}`) }inside the outerif` would fail fast.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 56 - 158, The code path that
handles authType starting with "azure-active-directory" (see authType variable
and mssqlConfig.authentication assignments) does not handle unknown subtypes and
can leave mssqlConfig without authentication; add a terminal else branch after
the existing azure-active-directory-* branches that throws a clear Error (e.g.
mentioning the unsupported authType) so unknown or misspelled Azure AD subtypes
fail fast; ensure the thrown error references authType so callers get an
explicit message instead of a later opaque tedious error.

Comment on lines +76 to +87
try {
const azureIdentity = await import("@azure/identity")
const credential = new azureIdentity.DefaultAzureCredential(
config.azure_client_id
? { managedIdentityClientId: config.azure_client_id as string }
: undefined,
)
const tokenResponse = await credential.getToken("https://database.windows.net/.default")
token = tokenResponse?.token
} catch {
// @azure/identity unavailable or browser bundle — fall through
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Swallowing every @azure/identity error makes failed logins hard to diagnose.

The bare catch {} at Line 85 is intended to tolerate "module missing" / "browser bundle" cases, but it also hides genuine authentication failures (expired cached creds, AADSTS* errors, network/proxy issues). When both paths fail, the user gets the generic "install @azure/identity or run az login" message with no hint that identity was reachable and rejected them. Consider capturing the error and including it in the final thrown message (or logging at debug level) so the real cause surfaces.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 76 - 87, The empty catch
around the `@azure/identity` import and credential.getToken is hiding real
authentication errors; change the catch to capture the thrown error (e.g., catch
(err) { azureIdentityErr = err }) and preserve that error in a local variable,
then either append its message/details to the final thrown error or log it at
debug level when token remains undefined; update the block that currently throws
the generic "install `@azure/identity` or run az login" message to include the
captured azureIdentityErr (or its message/stack) so real AAD/authentication
failures are visible while still tolerating missing-module/browser-bundle cases.

Comment on lines +162 to +167
if (MssqlConnectionPool) {
pool = new MssqlConnectionPool(mssqlConfig)
await pool.connect()
} else {
pool = await mssql.connect(mssqlConfig)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

mssql npm package v12 ConnectionPool named export availability

💡 Result:

Yes, the mssql npm package version 12 provides ConnectionPool as a named export. It can be imported using ES6 named import syntax: import { ConnectionPool } from 'mssql'; or accessed as sql.ConnectionPool after default import: const sql = require('mssql'); new sql.ConnectionPool(config);

Citations:


🏁 Script executed:

# First, let's locate and examine the sqlserver.ts file
find . -name "sqlserver.ts" -type f

Repository: AltimateAI/altimate-code

Length of output: 103


🏁 Script executed:

# Check the specific lines mentioned in the review (162-167) with surrounding context
sed -n '150,175p' packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 955


🏁 Script executed:

# Let's see the imports at the top of the file to understand where MssqlConnectionPool comes from
head -50 packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 1808


🏁 Script executed:

# Search for the function/method containing these lines to understand the full context
sed -n '140,180p' packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 1540


🏁 Script executed:

# Check the package.json for mssql version
cat packages/drivers/package.json | grep -A 5 -B 5 "mssql"

Repository: AltimateAI/altimate-code

Length of output: 354


🏁 Script executed:

# Search for any comments or context about the fallback or isolation bug
rg "ConnectionPool" packages/drivers/src/sqlserver.ts -B 3 -A 3

Repository: AltimateAI/altimate-code

Length of output: 980


🏁 Script executed:

# Look for any tests related to this fallback
find packages/drivers -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "mssql\|ConnectionPool" 2>/dev/null

Repository: AltimateAI/altimate-code

Length of output: 112


🏁 Script executed:

# Check git log for recent changes to sqlserver.ts
git log --oneline -20 packages/drivers/src/sqlserver.ts 2>/dev/null || echo "Git history not available"

Repository: AltimateAI/altimate-code

Length of output: 147


🏁 Script executed:

# Let's examine the test file to understand expected behavior
cat packages/drivers/test/sqlserver-unit.test.ts

Repository: AltimateAI/altimate-code

Length of output: 16662


🏁 Script executed:

# Check git history with more detail to understand context of the change
git log --all --oneline -30 | grep -i "mssql\|connection\|isolation" || git log --all --oneline -30 packages/drivers/src/

Repository: AltimateAI/altimate-code

Length of output: 148


🏁 Script executed:

# Look for any issues or comments in code about the fallback scenario
rg "fallback|global.*pool" packages/drivers/src/sqlserver.ts -i

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Search the entire drivers package for any documentation about this isolation issue
find packages/drivers -type f \( -name "*.md" -o -name "*.txt" \) | xargs grep -l "isolation\|global.*pool" 2>/dev/null || echo "No docs found"

Repository: AltimateAI/altimate-code

Length of output: 81


🏁 Script executed:

# Let's check for any PR or issue references in recent commits
git log --all --grep="isolation\|pool\|ConnectionPool" --oneline 2>/dev/null | head -20 || echo "No matching commits"

Repository: AltimateAI/altimate-code

Length of output: 1333


🏁 Script executed:

# Search for any comments about the specific bug being fixed
rg "bug|issue|fix" packages/drivers/src/sqlserver.ts -i -B 2 -A 2

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Look for mssql documentation references or comments about connect() vs ConnectionPool
rg "connect\(\)|mssql\." packages/drivers/src/sqlserver.ts -B 1 -A 1

Repository: AltimateAI/altimate-code

Length of output: 607


🏁 Script executed:

# Check if there are any integration tests that might show the different behaviors
find packages/drivers/test -name "*.test.*" -o -name "*.integration.*" | head -5

Repository: AltimateAI/altimate-code

Length of output: 204


🏁 Script executed:

# Check if there are any comments or documentation about why this fallback exists
git log -p --all -- packages/drivers/src/sqlserver.ts | grep -A 10 -B 10 "fallback\|else.*mssql.connect" | head -50 || echo "No relevant history found"

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Look for any version-specific handling or compatibility notes
rg "version|@12|mssql" packages/drivers/src/sqlserver.ts -i

Repository: AltimateAI/altimate-code

Length of output: 1155


🏁 Script executed:

# Check the mssql types to understand the difference between named export and default
find . -path "*/node_modules/mssql*" -name "*.d.ts" -o -name "package.json" | grep mssql | head -5

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Look at how other drivers handle optional peer dependencies and fallbacks
ls -la packages/drivers/src/ | grep -E "\.ts$"

Repository: AltimateAI/altimate-code

Length of output: 988


🏁 Script executed:

# Search for any recent PR or commit that added this fallback logic or introduced the isolation fix
git log --all --oneline -50 packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 639


🏁 Script executed:

# Check the full context of the connect function to see if there's any explanation
sed -n '1,100p' packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 4186


🏁 Script executed:

# Check the actual PR that introduced these changes - look for when the drivers were rewritten
git show 1b3a4ab --stat | grep sqlserver

Repository: AltimateAI/altimate-code

Length of output: 386


🏁 Script executed:

# Let's see if there's any documentation about why the defensive fallback was added
git show 1b3a4ab:packages/drivers/src/sqlserver.ts 2>/dev/null | head -100 || echo "File may not exist in that commit"

Repository: AltimateAI/altimate-code

Length of output: 2907


🏁 Script executed:

# Let's check when the ConnectionPool logic was added - search between the commit and now
git log --oneline --all -- packages/drivers/src/sqlserver.ts | head -20

Repository: AltimateAI/altimate-code

Length of output: 639


🏁 Script executed:

# Check the diff between the original and current to see when ConnectionPool was introduced
git show 1b3a4ab:packages/drivers/src/sqlserver.ts 2>/dev/null | grep -i "connectionpool" || echo "Not in original commit"

Repository: AltimateAI/altimate-code

Length of output: 90


🏁 Script executed:

# Find the commit that introduced ConnectionPool logic
git log -p --all -- packages/drivers/src/sqlserver.ts | grep -B 5 "MssqlConnectionPool = mod.ConnectionPool" | head -20

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Search for when ConnectionPool was first introduced in this file
git log --all -p -- packages/drivers/src/sqlserver.ts | grep -A 3 -B 3 "ConnectionPool" | head -40

Repository: AltimateAI/altimate-code

Length of output: 1003


🏁 Script executed:

# Let's check what changed between 1b3a4ab and current HEAD for sqlserver.ts
git diff 1b3a4ab HEAD -- packages/drivers/src/sqlserver.ts | head -100

Repository: AltimateAI/altimate-code

Length of output: 4780


🏁 Script executed:

# Find the specific commit that introduced ConnectionPool
git log --all --oneline --reverse -- packages/drivers/src/sqlserver.ts | tail -10

Repository: AltimateAI/altimate-code

Length of output: 639


🏁 Script executed:

# Find the exact commit that introduced ConnectionPool logic
git log --all -p -- packages/drivers/src/sqlserver.ts | grep -B 20 "MssqlConnectionPool = mod.ConnectionPool" | head -50

Repository: AltimateAI/altimate-code

Length of output: 879


🏁 Script executed:

# Check commit c438bb0 to see if it introduced ConnectionPool
git show c438bb0:packages/drivers/src/sqlserver.ts 2>/dev/null | grep -A 10 -B 2 "ConnectionPool" || echo "Not in c438bb0"

Repository: AltimateAI/altimate-code

Length of output: 82


🏁 Script executed:

# Check the commits between 1b3a4ab and current
git log --oneline 1b3a4ab..HEAD -- packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 147


🏁 Script executed:

# Check the specific commit 1977232 to see what changed with ConnectionPool
git show 1977232 -- packages/drivers/src/sqlserver.ts | grep -A 15 -B 5 "ConnectionPool"

Repository: AltimateAI/altimate-code

Length of output: 1847


🏁 Script executed:

# Get the full context of this commit
git show --stat 1977232 | head -30

Repository: AltimateAI/altimate-code

Length of output: 1738


🏁 Script executed:

# Check if there's a PR associated with this commit
git show 1977232 | head -50

Repository: AltimateAI/altimate-code

Length of output: 2529


🏁 Script executed:

# Let's find when ConnectionPool was actually first added
for commit in b907943 9aa1d39 53c38a8 b5242fa; do
  echo "=== Checking commit $commit ==="
  git show $commit:packages/drivers/src/sqlserver.ts 2>/dev/null | grep -c "ConnectionPool" && echo "Found ConnectionPool" || echo "No ConnectionPool"
done

Repository: AltimateAI/altimate-code

Length of output: 275


🏁 Script executed:

# Let's check the current state and understand the flow better
# Look at how mssql.connect() is documented vs ConnectionPool
web_search
mssql npm tedious package global pool default vs ConnectionPool instance isolation

Repository: AltimateAI/altimate-code

Length of output: 162


🌐 Web query:

mssql npm tedious package global pool default vs ConnectionPool instance isolation

💡 Result:

In the context of MSSQL connections using the 'tedious' npm package (the default driver for the 'mssql' package), there is no built-in "global pool default" in tedious itself. Tedious provides individual Connection objects, and connection pooling is handled at a higher level. The 'mssql' package (which uses tedious internally) provides ConnectionPool instances. Each new ConnectionPool(config).connect creates a separate, isolated pool of tedious Connections. These pools do not share connections with each other; each manages its own pool of TDS (tedious) connections. There is a "global pool" accessible via mssql.connect(config), which is a convenience that returns the same pool instance for repeated calls with the same config (in newer versions), but only one global pool exists at a time. Using new ConnectionPool instances bypasses this and gives full isolation. A separate 'tedious-connection-pool' npm package exists (unmaintained since 2020), which provides its own ConnectionPool class wrapping tedious Connections. Each instance of this is also isolated. Best practice: Use a single app-wide ConnectionPool instance (often as a module-exported singleton via Node's module caching) for efficiency, or manage multiple isolated pools (e.g., for different databases) via a custom pool manager. Multiple pools do not share connections, ensuring isolation but consuming more resources if overused. Example of isolated pools in mssql: const pool1 = new sql.ConnectionPool(config1).connect; const pool2 = new sql.ConnectionPool(config2).connect; // pool1 and pool2 manage separate sets of tedious connections

Citations:


Drop the fallback else branch or throw when ConnectionPool is unavailable.

The fallback at line 165–166 to mssql.connect(mssqlConfig) uses a global shared pool, which defeats the per-database isolation that this change introduces. Since the code requires mssql@^12.0.0 where ConnectionPool is a stable named export, the fallback is unlikely to trigger; if it does, it silently reintroduces the isolation bug. Either throw an error or remove the else branch entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 162 - 167, Remove the
fallback to the global shared pool to avoid reintroducing cross-database
isolation; in the block that currently checks MssqlConnectionPool, ensure that
if MssqlConnectionPool is falsy you throw an explicit error instead of calling
mssql.connect, i.e., keep the branch that constructs and connects a new
MssqlConnectionPool(mssqlConfig) and replace the else branch with a throw that
references MssqlConnectionPool and mssql.connect in the message so reviewers can
quickly locate and understand the change.

Comment on lines +208 to +217
const sampleFlat = rows.length > 0 ? rows[0] : []
const namedKeys = recordset.length > 0 ? Object.keys(recordset[0]) : []
const columns =
rows.length > 0
? Object.keys(rows[0]).filter((k) => !k.startsWith("_"))
: (result.recordset?.columns
? Object.keys(result.recordset.columns)
: [])
const truncated = effectiveLimit > 0 && rows.length > effectiveLimit
const limitedRows = truncated ? rows.slice(0, effectiveLimit) : rows
namedKeys.length === sampleFlat.length
? namedKeys
: sampleFlat.length > 0
? sampleFlat.map((_: any, i: number) => `col_${i}`)
: (result.recordset?.columns
? Object.keys(result.recordset.columns)
: [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Mixed named + multi-unnamed columns lose their named headers.

When a row has both named columns and multiple unnamed aggregates (e.g. SELECT name, COUNT(*), SUM(x) FROM t{ name: "alice", "": [42, 100] }), namedKeys.length === 2 but sampleFlat.length === 3, so the fallback replaces every column with col_0..col_N — including the name column whose header was known. Consider preserving the known prefix and only synthesizing names for the flattened tail.

♻️ Suggested approach
-      const rows = limitedRecordset.map(flattenRow)
-      const sampleFlat = rows.length > 0 ? rows[0] : []
-      const namedKeys = recordset.length > 0 ? Object.keys(recordset[0]) : []
-      const columns =
-        namedKeys.length === sampleFlat.length
-          ? namedKeys
-          : sampleFlat.length > 0
-            ? sampleFlat.map((_: any, i: number) => `col_${i}`)
-            : (result.recordset?.columns
-                ? Object.keys(result.recordset.columns)
-                : [])
+      const rows = limitedRecordset.map(flattenRow)
+      const sampleFlat = rows.length > 0 ? rows[0] : []
+      const sampleRow = recordset[0] ?? {}
+      const columns: string[] = []
+      if (sampleFlat.length === 0) {
+        if (result.recordset?.columns) columns.push(...Object.keys(result.recordset.columns))
+      } else {
+        let i = 0
+        for (const [k, v] of Object.entries(sampleRow)) {
+          if (k === "" && Array.isArray(v)) {
+            for (let j = 0; j < v.length; j++) columns.push(`col_${i++}`)
+          } else {
+            columns.push(k === "" ? `col_${i}` : k)
+            i++
+          }
+        }
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/src/sqlserver.ts` around lines 208 - 217, The current
columns logic replaces known named headers when sampleFlat is longer (mixed
named + unnamed aggregates); change the construction in the columns branch to
preserve the existing named prefix (namedKeys) and only synthesize column names
for the extra flattened entries (e.g., generate col_i for indices >=
namedKeys.length up to sampleFlat.length-1), falling back to
result.recordset.columns only when both namedKeys and sampleFlat are empty;
update the conditional around sampleFlat/namedKeys where columns is assigned
(the expression that uses sampleFlat, namedKeys, recordset, and
result.recordset.columns) so known headers are retained and only the tail is
renamed.

Comment on lines +154 to +159
test("empty result returns correctly", async () => {
mockQueryResult = { recordset: [], recordset_columns: {} }
const result = await connector.execute("SELECT * FROM t")
expect(result.rows).toEqual([])
expect(result.truncated).toBe(false)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

recordset_columns isn't a real mssql response shape.

mssql exposes column metadata at result.recordset.columns, not as a sibling recordset_columns key. The driver code (Lines 215-216) looks up result.recordset?.columns, so the key set here has no effect — the test still passes only because recordset is empty. Either drop the field or rename it to mirror the real shape, otherwise future readers will copy the wrong pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/drivers/test/sqlserver-unit.test.ts` around lines 154 - 159, The
test "empty result returns correctly" uses a fake mssql response shape by
setting mockQueryResult.recordset_columns, but the driver reads
result.recordset?.columns; update the test to match real mssql shape by removing
recordset_columns and instead set mockQueryResult.recordset = [] and, if you
need to include column metadata, set mockQueryResult.recordset.columns = {} (or
leave absent for empty results) so the driver code that checks
result.recordset?.columns and the code paths in connector.execute behave against
the real response shape.

Comment on lines +595 to +600
case "sqlserver":
case "mssql":
case "tsql":
case "fabric":
// Style 23 = ISO-8601 (yyyy-mm-dd), locale-safe
return `${expr} = CONVERT(DATE, '${escaped}', 23)`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how sqlserver driver serializes Date values in row output.
fd -t f 'sqlserver.ts' packages/drivers/src | xargs rg -n -C4 -P '(Date|toISO|useUTC|dateStrings|TYPES|parseJSON|rowsAsArray)'

Repository: AltimateAI/altimate-code

Length of output: 52


🏁 Script executed:

find packages -name "sqlserver.ts" -o -name "*sqlserver*" -type f | head -20

Repository: AltimateAI/altimate-code

Length of output: 146


🏁 Script executed:

# Find the data-diff file first
find packages -name "data-diff.ts" -type f

Repository: AltimateAI/altimate-code

Length of output: 180


🏁 Script executed:

# Explore the drivers directory structure
ls -la packages/drivers/src/ 2>/dev/null || ls -la packages/*/src/ | head -30

Repository: AltimateAI/altimate-code

Length of output: 1103


🏁 Script executed:

cat -n packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 12990


🏁 Script executed:

sed -n '160,180p' packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 926


🏁 Script executed:

sed -n '580,610p' packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 975


🏁 Script executed:

grep -r "mssql" packages/drivers/package.json packages/*/package.json 2>/dev/null | grep -v node_modules

Repository: AltimateAI/altimate-code

Length of output: 175


🏁 Script executed:

# Find buildPartitionDiscoverySQL function
grep -n "buildPartitionDiscoverySQL\|DATETRUNC" packages/opencode/src/altimate/native/connections/data-diff.ts | head -20

Repository: AltimateAI/altimate-code

Length of output: 374


🏁 Script executed:

# Look for the full buildPartitionDiscoverySQL function
sed -n '430,480p' packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 1687


🏁 Script executed:

sed -n '531,570p' packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 1307


🏁 Script executed:

# Check mssql driver documentation for date handling options
grep -n "useUTC\|dateStrings\|parseJSON\|number" packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 256


🏁 Script executed:

# Verify DATETRUNC return type behavior and check if there's any post-query normalization
sed -n '690,730p' packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 1569


🏁 Script executed:

# Check what the mssql driver returns for DATETRUNC and verify String(Date) behavior
cat > /tmp/test_date.js << 'EOF'
const d = new Date('2024-01-01');
console.log("Date object:", d);
console.log("String(date):", String(d));
console.log("date.toISOString().slice(0, 10):", d.toISOString().slice(0, 10));
EOF
node /tmp/test_date.js

Repository: AltimateAI/altimate-code

Length of output: 226


🏁 Script executed:

# Verify mssql driver v12 documentation by looking at driver setup in sqlserver.ts
# Check if there are any driver options that would normalize dates to strings
grep -n "dateAsString\|useUTC\|legacyTypes" packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Look for any normalization in the normalize.ts file
grep -n "Date\|ISO\|dateStrings" packages/drivers/src/normalize.ts | head -20

Repository: AltimateAI/altimate-code

Length of output: 50


🏁 Script executed:

# Verify if there's a test that would catch this MSSQL date partition issue
grep -n "CONVERT\|buildPartitionWhereClause\|DATETRUNC" packages/opencode/src/altimate/native/connections/data-diff.ts | grep -A2 -B2 "595\|600"

Repository: AltimateAI/altimate-code

Length of output: 281


🏁 Script executed:

# Check if there are any tests for buildPartitionWhereClause with MSSQL
find packages -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "buildPartitionWhereClause\|data-diff" 2>/dev/null | head -5

Repository: AltimateAI/altimate-code

Length of output: 179


🏁 Script executed:

# Check test files to see if MSSQL partition tests exist
cat packages/opencode/test/altimate/data-diff-dialect.test.ts | head -100

Repository: AltimateAI/altimate-code

Length of output: 1762


🏁 Script executed:

# Search for MSSQL/SQL Server specific tests in data-diff tests
grep -n "sqlserver\|mssql\|tsql\|fabric\|CONVERT" packages/opencode/test/altimate/data-diff-dialect.test.ts

Repository: AltimateAI/altimate-code

Length of output: 526


🏁 Script executed:

# Check if there are any integration tests or mssql-specific tests
grep -r "fabric\|mssql\|sqlserver\|CONVERT.*DATE" packages/opencode/test/ --include="*.test.ts" --include="*.spec.ts"

Repository: AltimateAI/altimate-code

Length of output: 4251


🏁 Script executed:

# Verify the exact behavior by checking if there's any config or option to handle date parsing
grep -B5 -A5 "options.*encrypt\|options.*trustServerCertificate" packages/drivers/src/sqlserver.ts

Repository: AltimateAI/altimate-code

Length of output: 553


🏁 Script executed:

# Final verification: check if there's any Date-to-ISO normalization elsewhere in the executeQuery path
grep -B10 -A10 "return result.rows.map" packages/opencode/src/altimate/native/connections/data-diff.ts

Repository: AltimateAI/altimate-code

Length of output: 867


Fix MSSQL/Fabric date partition literal format.

When buildPartitionDiscoverySQL executes for MSSQL/Fabric with date granularity, it returns DATETRUNC(MONTH, [col]) which the mssql v12 driver converts to a JS Date object. In executeQuery, this is stringified via String(v), producing "Mon Jan 01 2024 00:00:00 GMT+0000 (UTC)".

However, buildPartitionWhereClause (line 595–600) passes this string directly into CONVERT(DATE, '${escaped}', 23). Style 23 expects ISO format (yyyy-mm-dd), so the query will fail at parse/convert time: Conversion failed when converting date ... from character string.

Normalize the literal to ISO format before use:

Suggested fix
   // date mode
   const expr = dateTruncExpr(granularity!, quotedCol, dialect)
-  const escaped = partitionValue.replace(/'/g, "''")
+  // Normalize JS Date/ISO timestamps to plain yyyy-mm-dd for dialect-safe casting
+  const isoDate = (() => {
+    const d = new Date(partitionValue)
+    return isNaN(d.getTime())
+      ? partitionValue
+      : d.toISOString().slice(0, 10)
+  })()
+  const escaped = isoDate.replace(/'/g, "''")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants